<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>空间导航能力心理学实验 - 二维码功能详解</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    :root {
      --primary: #3498db;
      --secondary: #2ecc71;
      --accent: #e74c3c;
      --dark: #2c3e50;
      --light: #ecf0f1;
      --panel: rgba(255, 255, 255, 0.95);
      --shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
    }

    body {
      background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
      background-size: 400% 400%;
      animation: gradientBG 15s ease infinite;
      color: var(--dark);
      min-height: 100vh;
      padding: 20px;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    @keyframes gradientBG {
      0% {
        background-position: 0% 50%;
      }

      50% {
        background-position: 100% 50%;
      }

      100% {
        background-position: 0% 50%;
      }
    }

    .container {
      width: 100%;
      max-width: 1200px;
      display: flex;
      flex-direction: column;
      gap: 25px;
    }

    header {
      background: var(--panel);
      border-radius: 20px;
      padding: 25px;
      text-align: center;
      box-shadow: var(--shadow);
      border: 2px solid rgba(255, 255, 255, 0.6);
    }

    h1 {
      color: var(--dark);
      font-size: 2.8rem;
      margin-bottom: 15px;
      text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
      background: linear-gradient(90deg, var(--primary), var(--secondary));
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .subtitle {
      color: var(--dark);
      font-size: 1.3rem;
      max-width: 800px;
      margin: 0 auto;
      line-height: 1.6;
    }

    .qr-explanation {
      background: rgba(255, 255, 255, 0.2);
      border-radius: 15px;
      padding: 25px;
      margin: 20px 0;
      border-left: 5px solid var(--secondary);
    }

    .qr-explanation h2 {
      color: white;
      margin-bottom: 15px;
      text-align: center;
    }

    .qr-section {
      display: flex;
      justify-content: space-around;
      flex-wrap: wrap;
      gap: 30px;
      margin-top: 20px;
    }

    .qr-card {
      background: var(--panel);
      border-radius: 15px;
      padding: 20px;
      text-align: center;
      box-shadow: var(--shadow);
      width: 300px;
      transition: transform 0.3s ease;
    }

    .qr-card:hover {
      transform: translateY(-10px);
    }

    .qr-card h3 {
      color: var(--primary);
      margin-bottom: 15px;
    }

    .qr-description {
      margin: 15px 0;
      line-height: 1.6;
    }

    .qr-image {
      width: 200px;
      height: 200px;
      margin: 0 auto;
      background: white;
      padding: 10px;
      border-radius: 10px;
    }

    .qr-image img {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }

    .qr-url {
      margin-top: 10px;
      font-size: 0.9rem;
      word-break: break-all;
      color: var(--dark);
    }

    .main-content {
      display: flex;
      gap: 25px;
      flex-wrap: wrap;
    }

    .panel {
      background: var(--panel);
      border-radius: 20px;
      padding: 25px;
      box-shadow: var(--shadow);
      display: flex;
      flex-direction: column;
      gap: 20px;
      border: 2px solid rgba(255, 255, 255, 0.6);
    }

    .maze-section {
      flex: 1;
      min-width: 350px;
    }

    .panel-title {
      display: flex;
      align-items: center;
      gap: 15px;
      color: var(--primary);
      font-size: 1.7rem;
      margin-bottom: 10px;
      padding-bottom: 15px;
      border-bottom: 2px solid #eee;
    }

    #maze-canvas {
      background: #1a2536;
      border-radius: 15px;
      border: 4px solid #34495e;
      box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5);
      width: 100%;
      height: 400px;
    }

    .controls {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 15px;
    }

    .btn {
      padding: 15px;
      border: none;
      border-radius: 12px;
      background: var(--primary);
      color: white;
      font-weight: bold;
      font-size: 1.1rem;
      cursor: pointer;
      transition: all 0.3s ease;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    }

    .btn:hover {
      transform: translateY(-5px);
      box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
    }

    .btn i {
      font-size: 1.3rem;
    }

    .btn.start {
      background: var(--secondary);
      background: linear-gradient(135deg, var(--secondary), #27ae60);
    }

    .btn.reset {
      background: var(--accent);
      background: linear-gradient(135deg, var(--accent), #c0392b);
    }

    .instructions {
      margin-top: 20px;
      background: rgba(255, 255, 255, 0.3);
      padding: 20px;
      border-radius: 15px;
      border-left: 5px solid var(--secondary);
    }

    .instructions h3 {
      margin-bottom: 15px;
      color: white;
      font-size: 1.4rem;
    }

    .instructions ul {
      padding-left: 25px;
      color: var(--light);
    }

    .instructions li {
      margin-bottom: 12px;
      font-size: 1.1rem;
      line-height: 1.6;
    }

    .highlight {
      background: rgba(255, 255, 255, 0.3);
      padding: 2px 6px;
      border-radius: 4px;
      font-weight: bold;
    }

    footer {
      background: var(--panel);
      border-radius: 20px;
      padding: 25px;
      text-align: center;
      margin-top: 20px;
      width: 100%;
      box-shadow: var(--shadow);
      border: 2px solid rgba(255, 255, 255, 0.6);
    }

    footer p {
      margin-bottom: 20px;
    }

    .qr-container {
      display: flex;
      justify-content: center;
      gap: 50px;
      flex-wrap: wrap;
    }

    @media (max-width: 768px) {
      .main-content {
        flex-direction: column;
      }

      .controls {
        grid-template-columns: repeat(2, 1fr);
      }

      h1 {
        font-size: 2.2rem;
      }

      .qr-section {
        flex-direction: column;
        align-items: center;
      }
    }
  </style>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>

<body>
  <div class="container">
    <header>
      <h1><i class="fas fa-brain"></i> 空间导航能力心理学实验</h1>
      <p class="subtitle">二维码功能详解与实验平台</p>
    </header>

    <div class="qr-explanation">
      <h2><i class="fas fa-qrcode"></i> 二维码在心理学实验中的作用</h2>
      <div class="qr-section">
        <div class="qr-card">
          <h3><i class="fas fa-flask"></i> 实验参与二维码</h3>
          <div class="qr-description">
            此二维码包含实验平台的URL链接，当印刷在论文中时，读者可以使用手机扫描直接参与实验
          </div>
          <div class="qr-image">
            <img
              src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=https://psych-maze-experiment.example.com"
              alt="实验参与二维码">
          </div>
          <div class="qr-url">https://psych-maze-experiment.example.com</div>
        </div>

        <div class="qr-card">
          <h3><i class="fas fa-file-alt"></i> 研究论文二维码</h3>
          <div class="qr-description">
            此二维码链接到相关研究论文，方便实验参与者和读者了解研究背景和结果
          </div>
          <div class="qr-image">
            <img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=https://research-paper.example.com"
              alt="研究论文二维码">
          </div>
          <div class="qr-url">https://research-paper.example.com</div>
        </div>
      </div>
    </div>

    <div class="main-content">
      <div class="panel maze-section">
        <div class="panel-title">
          <i class="fas fa-chess-board"></i>
          <h2>迷宫导航实验</h2>
        </div>

        <canvas id="maze-canvas"></canvas>

        <div class="controls">
          <button class="btn start" id="start-btn">
            <i class="fas fa-play"></i> 开始实验
          </button>
          <button class="btn" id="new-maze-btn">
            <i class="fas fa-sync-alt"></i> 新迷宫
          </button>
          <button class="btn reset" id="reset-btn">
            <i class="fas fa-undo"></i> 重置位置
          </button>
          <button class="btn" id="save-btn">
            <i class="fas fa-save"></i> 保存数据
          </button>
        </div>

        <div class="instructions">
          <h3><i class="fas fa-info-circle"></i> 实验说明</h3>
          <ul>
            <li>使用键盘<span class="highlight">方向键或WASD键</span>在迷宫中导航</li>
            <li>从<span class="highlight" style="color: #3498db">蓝色起点</span>出发，到达<span class="highlight"
                style="color: #2ecc71">绿色终点</span></li>
            <li>尝试以<span class="highlight">最短时间和最少步数</span>完成迷宫</li>
            <li>您的导航数据将用于心理学研究</li>
          </ul>
        </div>
      </div>
    </div>

    <footer>
      <p>心理学研究项目 © 2023 | 本实验数据仅用于科学研究，所有参与者数据将被匿名处理</p>

      <div class="qr-container">
        <div class="qr-card">
          <h3>参与实验</h3>
          <div class="qr-image">
            <img
              src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://psych-maze-experiment.example.com"
              alt="实验参与二维码">
          </div>
          <p>扫码参与实验</p>
        </div>

        <div class="qr-card">
          <h3>研究论文</h3>
          <div class="qr-image">
            <img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://research-paper.example.com"
              alt="研究论文二维码">
          </div>
          <p>扫码查看研究论文</p>
        </div>
      </div>
    </footer>
  </div>

  <script>
    // 迷宫配置
    const config = {
      canvas: null,
      cellSize: 30,
      width: 21,
      height: 15,
      playerColor: '#e74c3c',
      startColor: '#3498db',
      endColor: '#2ecc71',
      wallColor: '#34495e',
      pathColor: '#2c3e50',
      pathHistoryColor: 'rgba(155, 89, 182, 0.7)'
    };

    // 游戏状态
    const gameState = {
      maze: [],
      playerPos: { x: 1, y: 1 },
      startPos: { x: 1, y: 1 },
      endPos: { x: 19, y: 13 },
      pathHistory: [],
      steps: 0,
      wallHits: 0,
      deadEnds: 0,
      startTime: 0,
      endTime: 0,
      completed: false,
      ctx: null,
      lastMoveTime: 0,
      moveDelay: 100
    };

    // 初始化游戏
    function initGame() {
      try {
        // 1. 获取Canvas元素
        config.canvas = document.getElementById('maze-canvas');
        if (!config.canvas) {
          throw new Error("未找到Canvas元素！");
        }

        // 2. 设置Canvas尺寸
        config.canvas.width = config.width * config.cellSize;
        config.canvas.height = config.height * config.cellSize;

        // 3. 获取Canvas上下文
        gameState.ctx = config.canvas.getContext('2d');
        if (!gameState.ctx) {
          throw new Error("无法获取Canvas上下文！");
        }

        // 4. 生成迷宫
        generateMaze();

        // 5. 初始化玩家位置
        gameState.playerPos = { ...gameState.startPos };
        gameState.pathHistory = [{ ...gameState.playerPos }];

        // 6. 绘制初始迷宫
        drawMaze();

        // 7. 添加键盘事件监听
        window.addEventListener('keydown', handleKeyPress);

        // 8. 按钮事件绑定
        document.getElementById('start-btn').addEventListener('click', startExperiment);
        document.getElementById('new-maze-btn').addEventListener('click', generateNewMaze);
        document.getElementById('reset-btn').addEventListener('click', resetPlayer);
        document.getElementById('save-btn').addEventListener('click', saveData);

      } catch (error) {
        console.error("初始化失败:", error);
        alert(`游戏初始化失败: ${error.message}`);
      }
    }

    // 迷宫生成算法
    function generateMaze() {
      try {
        // 初始化迷宫 (1表示墙，0表示路径)
        gameState.maze = Array(config.height).fill().map(() => Array(config.width).fill(1));

        // 设置起点和终点
        gameState.startPos = { x: 1, y: 1 };
        gameState.endPos = { x: config.width - 2, y: config.height - 2 };

        // 深度优先搜索生成迷宫
        const stack = [gameState.startPos];
        gameState.maze[gameState.startPos.y][gameState.startPos.x] = 0;

        while (stack.length > 0) {
          const { x, y } = stack[stack.length - 1];

          // 查找未访问的邻居
          const neighbors = [];
          for (const [dx, dy] of [[0, 2], [2, 0], [0, -2], [-2, 0]]) {
            const nx = x + dx;
            const ny = y + dy;

            if (nx > 0 && nx < config.width - 1 && ny > 0 && ny < config.height - 1 && gameState.maze[ny][nx] === 1) {
              neighbors.push({ x: nx, y: ny, bx: x + dx / 2, by: y + dy / 2 });
            }
          }

          if (neighbors.length > 0) {
            // 随机选择一个邻居
            const { x: nx, y: ny, bx, by } = neighbors[Math.floor(Math.random() * neighbors.length)];
            gameState.maze[ny][nx] = 0;
            gameState.maze[by][bx] = 0;
            stack.push({ x: nx, y: ny });
          } else {
            stack.pop();
          }
        }

        // 设置出口
        gameState.maze[gameState.endPos.y][gameState.endPos.x] = 2;

        // 重置游戏状态
        resetGameState();
      } catch (error) {
        console.error("迷宫生成失败:", error);
        throw new Error(`迷宫生成失败: ${error.message}`);
      }
    }

    // 绘制迷宫
    function drawMaze() {
      try {
        // 确保上下文存在
        if (!gameState.ctx) {
          gameState.ctx = config.canvas.getContext('2d');
          if (!gameState.ctx) return;
        }

        const ctx = gameState.ctx;
        const cellSize = config.cellSize;

        // 清除画布
        ctx.clearRect(0, 0, config.canvas.width, config.canvas.height);

        // 绘制迷宫
        for (let y = 0; y < config.height; y++) {
          for (let x = 0; x < config.width; x++) {
            ctx.fillStyle = gameState.maze[y][x] === 1 ? config.wallColor : config.pathColor;
            ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);

            // 绘制墙壁边框
            ctx.strokeStyle = '#2c3e50';
            ctx.lineWidth = 1;
            ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);

            // 绘制终点
            if (gameState.maze[y][x] === 2) {
              ctx.fillStyle = config.endColor;
              ctx.beginPath();
              ctx.arc(
                x * cellSize + cellSize / 2,
                y * cellSize + cellSize / 2,
                cellSize / 2 - 4,
                0,
                Math.PI * 2
              );
              ctx.fill();
            }
          }
        }

        // 绘制起点
        ctx.fillStyle = config.startColor;
        ctx.beginPath();
        ctx.arc(
          gameState.startPos.x * cellSize + cellSize / 2,
          gameState.startPos.y * cellSize + cellSize / 2,
          cellSize / 2 - 4,
          0,
          Math.PI * 2
        );
        ctx.fill();

        // 绘制路径历史
        if (gameState.pathHistory.length > 1) {
          ctx.strokeStyle = config.pathHistoryColor;
          ctx.lineWidth = 4;
          ctx.lineCap = 'round';
          ctx.beginPath();
          ctx.moveTo(
            gameState.pathHistory[0].x * cellSize + cellSize / 2,
            gameState.pathHistory[0].y * cellSize + cellSize / 2
          );

          for (let i = 1; i < gameState.pathHistory.length; i++) {
            ctx.lineTo(
              gameState.pathHistory[i].x * cellSize + cellSize / 2,
              gameState.pathHistory[i].y * cellSize + cellSize / 2
            );
          }
          ctx.stroke();
        }

        // 绘制玩家
        ctx.fillStyle = config.playerColor;
        ctx.beginPath();
        ctx.arc(
          gameState.playerPos.x * cellSize + cellSize / 2,
          gameState.playerPos.y * cellSize + cellSize / 2,
          cellSize / 3,
          0,
          Math.PI * 2
        );
        ctx.fill();
      } catch (error) {
        console.error("绘制迷宫失败:", error);
      }
    }

    // 开始实验函数
    function startExperiment() {
      try {
        if (gameState.completed) {
          return;
        }

        gameState.startTime = Date.now();
        gameState.completed = false;

        alert("实验已开始！使用方向键或WASD键导航。");
      } catch (error) {
        console.error("开始实验失败:", error);
      }
    }

    // 生成新迷宫
    function generateNewMaze() {
      generateMaze();
      drawMaze();
    }

    // 重置游戏状态
    function resetGameState() {
      gameState.playerPos = { ...gameState.startPos };
      gameState.pathHistory = [{ ...gameState.playerPos }];
      gameState.steps = 0;
      gameState.wallHits = 0;
      gameState.deadEnds = 0;
      gameState.startTime = 0;
      gameState.endTime = 0;
      gameState.completed = false;
    }

    // 重置玩家位置
    function resetPlayer() {
      gameState.playerPos = { ...gameState.startPos };
      gameState.pathHistory = [{ ...gameState.playerPos }];
      drawMaze();
    }

    // 处理按键事件
    function handleKeyPress(e) {
      if (gameState.completed || !gameState.startTime) return;

      const now = Date.now();
      if (now - gameState.lastMoveTime < gameState.moveDelay) return;

      gameState.lastMoveTime = now;

      let dx = 0, dy = 0;

      switch (e.key) {
        case 'ArrowUp':
        case 'w':
        case 'W':
          dy = -1;
          break;
        case 'ArrowDown':
        case 's':
        case 'S':
          dy = 1;
          break;
        case 'ArrowLeft':
        case 'a':
        case 'A':
          dx = -1;
          break;
        case 'ArrowRight':
        case 'd':
        case 'D':
          dx = 1;
          break;
        default:
          return;
      }

      movePlayer(dx, dy);
    }

    // 移动玩家
    function movePlayer(dx, dy) {
      const newX = gameState.playerPos.x + dx;
      const newY = gameState.playerPos.y + dy;

      // 检查边界
      if (newX < 0 || newX >= config.width || newY < 0 || newY >= config.height) {
        gameState.wallHits++;
        return;
      }

      // 检查墙壁
      if (gameState.maze[newY][newX] === 1) {
        gameState.wallHits++;
        return;
      }

      // 有效移动
      gameState.playerPos.x = newX;
      gameState.playerPos.y = newY;
      gameState.pathHistory.push({ x: newX, y: newY });
      gameState.steps++;

      // 检查是否到达终点
      if (newX === gameState.endPos.x && newY === gameState.endPos.y) {
        gameState.completed = true;
        gameState.endTime = Date.now();
        const completionTime = (gameState.endTime - gameState.startTime) / 1000;
        alert(`恭喜！您完成了迷宫！\n时间: ${completionTime.toFixed(1)}秒\n步数: ${gameState.steps}`);
      }

      drawMaze();
    }

    // 保存数据
    function saveData() {
      alert("实验数据已保存！\n在真实环境中，数据将被发送到研究数据库。");
    }

    // 生成二维码函数
    function generateQRCode(elementId, url) {
      const container = document.getElementById(elementId);
      if (!container) return;

      container.innerHTML = `
                <img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(url)}" 
                     alt="QR Code for ${url}">
            `;
    }

    // 确保在页面完全加载后初始化
    window.addEventListener('load', function () {
      initGame();

      // 设置二维码
      generateQRCode('experiment-qr', 'https://psych-maze-experiment.example.com');
      generateQRCode('paper-qr', 'https://research-paper.example.com');
    });
  </script>
</body>

</html>